import datetime

from dateutil.tz import tzlocal

from dojo.models import Test
from dojo.tools.github_vulnerability.parser import GithubVulnerabilityParser
from unittests.dojo_test_case import DojoTestCase, get_unit_tests_scans_path


class TestGithubVulnerabilityParser(DojoTestCase):
    def test_parse_file_with_no_vuln_has_no_findings(self):
        """Sample with zero vulnerability"""
        with (get_unit_tests_scans_path("github_vulnerability") / "github-0-vuln.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(0, len(findings))

    def test_parse_file_with_one_vuln_has_one_findings(self):
        """Sample with one vulnerability"""
        with (get_unit_tests_scans_path("github_vulnerability") / "github-1-vuln.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(1, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "Critical severity vulnerability that affects package")
                self.assertEqual(
                    finding.description,
                    "This is a sample description for sample description from Github API.",
                )
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(finding.component_name, "package")
                self.assertEqual(finding.unique_id_from_tool, "aabbccddeeff1122334401")

    def test_parse_file_with_one_vuln_has_one_finding_and_dependabot_direct_link(self):
        """Sample with dependabot PR and repository alert link"""
        with (get_unit_tests_scans_path("github_vulnerability") / "github-1-vuln-repo-dependabot-link.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(1, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "Critical severity vulnerability that affects package")
                expected_desc = (
                    "Repo Alert: [https://github.com/OWASP/test-repository/security/dependabot/1]"
                    "(https://github.com/OWASP/test-repository/security/dependabot/1)\n"
                    "Fix PR: [https://github.com/OWASP/test-repository/pull/1]"
                    "(https://github.com/OWASP/test-repository/pull/1)\n"
                    "This is a sample description for sample description from Github API."
                )
                self.assertEqual(finding.description, expected_desc)
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(finding.component_name, "package")
                self.assertEqual(finding.unique_id_from_tool, "aabbccddeeff1122334401")

    def test_parse_file_with_multiple_vuln_has_multiple_findings(self):
        """Sample with five vulnerability"""
        with (get_unit_tests_scans_path("github_vulnerability") / "github-5-vuln.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(5, len(findings))

    def test_parse_file_issue2984(self):
        with (get_unit_tests_scans_path("github_vulnerability") / "github_issue2984.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(4, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "XXXXXXXXXXXXXXX")
                self.assertEqual(finding.severity, "Medium")
                self.assertEqual(finding.unique_id_from_tool, "xxxxxxxxx")
            with self.subTest(i=1):
                finding = findings[1]
                self.assertEqual(finding.title, "AMSVNASCMASNCADNNJSADC")
                self.assertEqual(finding.severity, "Medium")
                self.assertEqual(finding.unique_id_from_tool, "AFDSFSDAFSDASFDAFSDASFD=")
            with self.subTest(i=3):
                finding = findings[3]
                self.assertEqual(finding.title, "SDKPKÁSMNMKSDANJDOPASJOKNDOSAJ")
                self.assertEqual(finding.severity, "Medium")
                self.assertEqual(finding.unique_id_from_tool, "DASFMMFKLNKDSAKFSDLANJKKFDSNJSAKDFNJKDFS=")

    def test_parse_file_search(self):
        with (get_unit_tests_scans_path("github_vulnerability") / "github_search.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(2, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "Deserialization of Untrusted Data in Log4j")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-2qrg-x229-3v8q")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2019-17571")
                self.assertEqual(finding.component_name, "log4j:log4j")
                self.assertEqual(
                    finding.unique_id_from_tool, "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQyMDg2Nzc5NzY=",
                )
            with self.subTest(i=1):
                finding = findings[1]
                self.assertEqual(finding.title, "Deserialization of Untrusted Data in Log4j")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-2qrg-x229-3v8q")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2019-17571")
                self.assertEqual(finding.component_name, "log4j:log4j")
                self.assertEqual(
                    finding.unique_id_from_tool, "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQ1NTE5NTI2OTM=",
                )

    def test_parse_file_search2(self):
        """Search result with more data/attributes"""
        with (get_unit_tests_scans_path("github_vulnerability") / "github_search2.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(2, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "Deserialization of Untrusted Data in Log4j")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-2qrg-x229-3v8q")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2019-17571")
                self.assertEqual(finding.component_name, "log4j:log4j")
                self.assertEqual(
                    finding.unique_id_from_tool, "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQyMDg2Nzc5NzY=",
                )
            with self.subTest(i=1):
                finding = findings[1]
                self.assertEqual(finding.title, "Deserialization of Untrusted Data in Log4j")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-2qrg-x229-3v8q")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2019-17571")
                self.assertEqual(finding.component_name, "log4j:log4j")
                self.assertEqual(
                    finding.unique_id_from_tool, "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQ1NTE5NTI2OTM=",
                )

    def test_parse_file_search3(self):
        """Search result with more data/attributes"""
        with (get_unit_tests_scans_path("github_vulnerability") / "github_search3.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(2, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "Deserialization of Untrusted Data in Log4j")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-2qrg-x229-3v8q")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2019-17571")
                self.assertEqual(finding.component_name, "log4j:log4j")
                self.assertEqual(finding.cvssv3, "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H")
                self.assertEqual(finding.file_path, "gogoph-crawler/pom.xml")
                self.assertEqual(
                    finding.unique_id_from_tool, "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQyMDg2Nzc5NzY=",
                )
            with self.subTest(i=1):
                finding = findings[1]
                self.assertEqual(finding.title, "Deserialization of Untrusted Data in Log4j")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-2qrg-x229-3v8q")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2019-17571")
                self.assertEqual(finding.component_name, "log4j:log4j")
                self.assertEqual(finding.cvssv3, "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H")
                self.assertEqual(finding.file_path, "gogoph/pom.xml")
                self.assertEqual(
                    finding.unique_id_from_tool, "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQ1NTE5NTI2OTM=",
                )

    def test_parse_file_search4_null_cvss_vector(self):
        """Search result with more data/attributes"""
        with (get_unit_tests_scans_path("github_vulnerability") / "github_search4_null_cvss_vector.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(2, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "Deserialization of Untrusted Data in Log4j")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-2qrg-x229-3v8q")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2019-17571")
                self.assertEqual(finding.component_name, "log4j:log4j")
                self.assertEqual(finding.cvssv3, None)
                self.assertEqual(finding.file_path, "gogoph-crawler/pom.xml")
                self.assertEqual(
                    finding.unique_id_from_tool, "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQyMDg2Nzc5NzY=",
                )
            with self.subTest(i=1):
                finding = findings[1]
                self.assertEqual(finding.title, "Deserialization of Untrusted Data in Log4j")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-2qrg-x229-3v8q")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2019-17571")
                self.assertEqual(finding.component_name, "log4j:log4j")
                self.assertEqual(finding.cvssv3, "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H")
                self.assertEqual(finding.file_path, "gogoph/pom.xml")
                self.assertEqual(
                    finding.unique_id_from_tool, "MDI4OlJlcG9zaXRvcnlWdWxuZXJhYmlsaXR5QWxlcnQ1NTE5NTI2OTM=",
                )

    def test_parse_cwe_and_date(self):
        with (get_unit_tests_scans_path("github_vulnerability") / "github_h2.json").open(encoding="utf-8") as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(1, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "RCE in H2 Console")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-h376-j262-vhq6")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2021-42392")
                self.assertEqual(finding.component_name, "com.h2database:h2")
                self.assertEqual(finding.cvssv3, "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H")
                self.assertEqual(finding.cvssv3_score, 9.8)
                self.assertEqual(finding.cwe, 502)
                self.assertEqual(datetime.datetime(2022, 5, 9, 9, 43, 40, tzinfo=tzlocal()), finding.date)
                self.assertEqual(finding.file_path, "apache/cxf/syncope/cxf-syncope/pom.xml")
                self.assertEqual(finding.active, True)

    def test_parse_state(self):
        with (get_unit_tests_scans_path("github_vulnerability") / "github_shiro.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(1, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(
                    finding.title,
                    "Apache Shiro vulnerable to a specially crafted HTTP request causing an authentication bypass",
                )
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(len(finding.unsaved_vulnerability_ids), 2)
                self.assertEqual(finding.unsaved_vulnerability_ids[0], "GHSA-f6jp-j6w3-w9hm")
                self.assertEqual(finding.unsaved_vulnerability_ids[1], "CVE-2021-41303")
                self.assertEqual(finding.component_name, "org.apache.shiro:shiro-core")
                self.assertEqual(finding.cvssv3, "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H")
                self.assertEqual(finding.cvssv3_score, 9.8)
                self.assertEqual(finding.cwe, 287)
                self.assertEqual(datetime.datetime(2021, 9, 20, 20, 33, 13, tzinfo=tzlocal()), finding.date)
                self.assertEqual(finding.file_path, "apache/cxf/cxf-shiro/pom.xml")
                self.assertEqual(finding.active, False)
                self.assertEqual(finding.is_mitigated, True)

    def test_parser_version(self):
        with (get_unit_tests_scans_path("github_vulnerability") / "github-vuln-version.json").open(
            encoding="utf-8",
        ) as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(1, len(findings))
            for finding in findings:
                finding.clean()

            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "Pivotal Spring Framework contains unsafe Java deserialization methods")
                self.assertEqual(finding.severity, "Critical")
                self.assertEqual(finding.component_name, "org.springframework:spring-web")
                self.assertEqual(finding.component_version, "5.3.29")
                self.assertAlmostEqual(finding.epss_score, 0.00212, places=5)
                self.assertAlmostEqual(finding.epss_percentile, 0.44035, places=5)

    def test_parse_file_issue_9582(self):
        with (get_unit_tests_scans_path("github_vulnerability") / "issue_9582.json").open(encoding="utf-8") as testfile:
            parser = GithubVulnerabilityParser()
            findings = parser.get_findings(testfile, Test())
            self.assertEqual(2, len(findings))
            for finding in findings:
                finding.clean()
            with self.subTest(i=0):
                finding = findings[0]
                self.assertEqual(finding.title, "py/clear-text-storage-sensitive-data")
                self.assertEqual(finding.severity, "High")
